package org.rakam.util;
import com.google.common.base.Charsets;
import com.google.common.base.Throwables;
import com.google.common.hash.Hashing;
import io.netty.handler.codec.http.HttpResponseStatus;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Locale;
import java.util.Random;
import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
public final class CryptUtil
{
private static final Random random = new SecureRandom();
private CryptUtil() {}
public static String generateRandomKey(int length)
{
String key;
while ((key = new BigInteger(length * 5/*base 32,2^5*/, random).toString(32)).length() < length) {
;
}
return key;
}
public static String sha1(String value)
{
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
}
catch (NoSuchAlgorithmException e) {
throw Throwables.propagate(e);
}
return new String(md.digest(value.getBytes(StandardCharsets.UTF_8)));
}
public static String encryptWithHMacSHA1(String data, String secret)
{
try {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(rawHmac);
}
catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
throw Throwables.propagate(e);
}
}
public static String encryptToHex(String data, String secret, String hashType)
{
try {
if(secret == null || secret.isEmpty()) {
throw new RakamException("Secret is is empty", INTERNAL_SERVER_ERROR);
}
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes("UTF-8"), hashType);
Mac mac = Mac.getInstance(hashType);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data.getBytes("UTF-8"));
return DatatypeConverter.printHexBinary(rawHmac).toLowerCase(Locale.ENGLISH);
}
catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
throw Throwables.propagate(e);
}
}
public static String encryptAES(String data, String secretKey)
{
try {
byte[] secretKeys = Arrays.copyOfRange(Hashing.sha256().hashString(secretKey, Charsets.UTF_8)
.asBytes(), 0, 16);
final SecretKey secret = new SecretKeySpec(secretKeys, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
final AlgorithmParameters params = cipher.getParameters();
final byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
final byte[] cipherText = cipher.doFinal(data.getBytes(Charsets.UTF_8));
return DatatypeConverter.printHexBinary(iv) + DatatypeConverter.printHexBinary(cipherText);
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
public static String decryptAES(String data, String secretKey)
{
try {
byte[] secretKeys = Arrays.copyOfRange(Hashing.sha256().hashString(secretKey, Charsets.UTF_8)
.asBytes(), 0, 16);
// grab first 16 bytes - that's the IV
String hexedIv = data.substring(0, 32);
// grab everything else - that's the cipher-text (encrypted message)
String hexedCipherText = data.substring(32);
byte[] iv = DatatypeConverter.parseHexBinary(hexedIv);
byte[] cipherText = DatatypeConverter.parseHexBinary(hexedCipherText);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKeys, "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), Charsets.UTF_8);
}
catch (BadPaddingException e) {
throw new IllegalArgumentException("Secret key is invalid");
}
catch (Exception e) {
throw Throwables.propagate(e);
}
}
}